package com.fin.zw.intelligentbot.service;

import com.fin.zw.intelligentbot.entity.*;
import com.fin.zw.intelligentbot.enums.UserStatus;
import com.fin.zw.intelligentbot.model.request.LoginRequest;
import com.fin.zw.intelligentbot.model.request.PasswordRequest;
import com.fin.zw.intelligentbot.model.request.UserIds;
import com.fin.zw.intelligentbot.model.request.UserRoleRequest;
import com.fin.zw.intelligentbot.model.response.RoleInfoResponse;
import com.fin.zw.intelligentbot.model.session.SessionModel;
import com.fin.zw.intelligentbot.repository.AccountRepository;
import com.fin.zw.intelligentbot.repository.RoleInfoRepository;
import com.fin.zw.intelligentbot.repository.UserInfoRepository;
import com.fin.zw.intelligentbot.util.Base64Utils;
import com.fin.zw.intelligentbot.util.ClientIpUtil;
import com.querydsl.core.BooleanBuilder;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.patchca.color.SingleColorFactory;
import org.patchca.filter.predefined.CurvesRippleFilterFactory;
import org.patchca.font.RandomFontFactory;
import org.patchca.service.ConfigurableCaptchaService;
import org.patchca.utils.encoder.EncoderHelper;
import org.patchca.word.RandomWordFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.*;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.*;

@Service("loginService")
public class LoginService {

    private static HttpSession session = null;
    private final Logger log = LoggerFactory.getLogger(LoginService.class);
    @Autowired
    private AccountRepository accountRepository;

    @Autowired
    private RoleInfoRepository roleInfoRepository;

    @Autowired
    private UserInfoRepository userInfoRepository;

    @Autowired
    private RedisTemplate<String, SessionModel> sessionOperatorRedisTemplate;

    public LoginService() {
    }

    /**
     * 获取验证码
     *
     * @param request
     * @param response
     * @throws Exception
     */
    public void patchca(HttpServletRequest request,
                        HttpServletResponse response) throws Exception {
        session = request.getSession();
        ConfigurableCaptchaService cs = new ConfigurableCaptchaService();
        cs.setColorFactory(new SingleColorFactory(new Color(25, 60, 170)));
        cs.setFilterFactory(new CurvesRippleFilterFactory(cs.getColorFactory()));
        RandomFontFactory ff = new RandomFontFactory();
        ff.setMinSize(30);
        ff.setMaxSize(30);
        RandomWordFactory rwf = new RandomWordFactory();
        rwf.setMinLength(4);
        rwf.setMaxLength(4);
        cs.setWordFactory(rwf);
        cs.setFontFactory(ff);
        cs.setHeight(30);
        cs.setWidth(100);

        response.setContentType("image/png");
        response.setHeader("Cache-Control", "no-cache, no-store");
        response.setHeader("Pragma", "no-cache");
        long time = System.currentTimeMillis();
        response.setDateHeader("Last-Modified", time);
        response.setDateHeader("Date", time);
        response.setDateHeader("Expires", time);
        ServletOutputStream stream = response.getOutputStream();
        String validateCode = EncoderHelper.getChallangeAndWriteImage(cs,
                "png", stream);
        session.setAttribute("validateCode", validateCode);
        stream.flush();
    }


    /**
     * @param userIds
     */
    public void unlockUserInfo(UserIds userIds) {
        Iterator<Account> iterator = accountRepository.findAll(QAccount.account.id.in(userIds.getIds())).iterator();
        List<Account> list = new ArrayList<>();
        while (iterator.hasNext()) {
            Account account = iterator.next();
            account.setErrorCount(0);
            account.setUserStatus(UserStatus.USER_START);
            list.add(account);
        }
        accountRepository.saveAll(list);
    }

    /**
     * 充值密码
     *
     * @param userIds
     */
    public void resetPassword(UserIds userIds) {
        Iterator<Account> iterator = accountRepository.findAll(QAccount.account.id.in(userIds.getIds())).iterator();
        List<Account> list = new ArrayList<>();
        while (iterator.hasNext()) {
            Account account = iterator.next();
            BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
            account.setPassword(passwordEncoder.encode("888888"));
            list.add(account);
        }
        accountRepository.saveAll(list);
    }

    /**
     * 对账号进行赋角色
     *
     * @param request
     */
    public void assignmentRole(UserRoleRequest request) {
        Optional<Account> optional = accountRepository.findById(request.getAccountId());
        if (!optional.isPresent()) {
            log.info("选择的账号不存在....");
            throw new RuntimeException("选择的账号不存在!");
        }
        Account account = optional.get();
        if (request.getRoleIds() != null || !request.getRoleIds().isEmpty()) {
            Iterable<RoleInfo> iterable = roleInfoRepository.findAll(QRoleInfo.roleInfo.id.in(request.getRoleIds()));
            if (iterable.iterator().hasNext()) {
                Set<RoleInfo> set = new HashSet<>(IterableUtils.toList(iterable));
                account.setRoleInfoSet(set);
                accountRepository.save(account);
            }
        }
    }

    /**
     * 登录功能
     *
     * @param loginRequest
     * @param request
     * @return
     */
    public SessionModel login(LoginRequest loginRequest,
                              HttpServletRequest request) throws Exception {
        log.info("当前登录账号是：" + loginRequest.getLoginCode());
        String validateCode = (String) session.getAttribute("validateCode"); //获取验证码
        String loginIp = ClientIpUtil.ClientIp(request);
        BooleanBuilder builder = new BooleanBuilder();
        QUserInfo qUserInfo = QUserInfo.userInfo;
        builder.and(qUserInfo.account.name.eq(loginRequest.getLoginCode()));
        //账号和用户是一对一的，并且账号是不允许重复的。
        UserInfo userInfo = userInfoRepository.findOne(builder).orElseThrow(() -> new RuntimeException("该账号不存在"));
        SessionModel sessionModel = new SessionModel();
        Account account = userInfo.getAccount();
        if (account.getUserStatus().equals(UserStatus.USER_STOP)) {
            log.info("该账号已经被停用....");
            throw new RuntimeException("该用户已经被停用!");
        } else if (account.getUserStatus().equals(UserStatus.USER_LOCK)) {
            log.info("该用户已经被锁定....");
            throw new RuntimeException("该用户已经被锁定!");
        } else {
            account.setLatestIp(loginIp);
            account.setLatestTime(new Date());
            if (!loginRequest.getAuthCode().equals(validateCode)) {
                account.setErrorCount(account.getErrorCount() + 1);
                if (account.getErrorCount() > 5) {
                    account.setUserStatus(UserStatus.USER_LOCK);
                    account.setLockTime(new Date());
                }
                accountRepository.save(account);
                log.info("验证码不正确....");
                throw new RuntimeException("验证码不正确!");
            }
            BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
            if (!passwordEncoder.matches(loginRequest.getPassword(), account.getPassword())) {
                account.setErrorCount(account.getErrorCount() + 1);
                if (account.getErrorCount() > 5) {
                    account.setUserStatus(UserStatus.USER_LOCK);
                    account.setLockTime(new Date());
                }
                accountRepository.save(account);
                log.info("用户密码不正确....");
                throw new RuntimeException("用户密码不正确!");
            }
            if (account.getRoleInfoSet() == null || account.getRoleInfoSet().isEmpty()) {
                log.info("该账号没有权限，请联系管理员....");
                throw new RuntimeException("该账号没有权限，请联系管理员!");
            }

            setToken(loginRequest, userInfo, account, sessionModel);
        }
        account.setErrorCount(0);
        accountRepository.save(account);
        return sessionModel;
    }

    public String setToken(LoginRequest loginRequest, UserInfo userInfo, Account account, SessionModel sessionModel) throws UnsupportedEncodingException {
        String token = new String();
        Date old = DateTime.now().minusDays(30).toDate();//获取30天以前的时间
        if (StringUtils.isNotEmpty(account.getToken())
                && account.getTokenTime() != null
                && old.before(account.getTokenTime())) {
            token = account.getToken();
        } else {
            token = UUID.randomUUID().toString();
            account.setTokenTime(new Date());
            account.setToken(token);
        }
        Set<Resource> set = getResources(userInfo, token, sessionModel);
        List<Resource> resourceList = new ArrayList<>(set);
        resourceList.sort(Comparator.comparing(Resource::getSort));
        sessionModel.setResources(resourceList);
        if (Objects.nonNull(userInfo.getCompany())) {
            sessionModel.setCompanyId(userInfo.getCompany().getId());
            sessionModel.setCompanyName(userInfo.getCompany().getName());
            sessionModel.setCompanyCode(userInfo.getCompany().getCode());
        }
        sessionOperatorRedisTemplate.opsForValue().set("operator:token:" + token, sessionModel);
        if (StringUtils.isNotEmpty(loginRequest.getLoginCode())
                && StringUtils.isNotEmpty(loginRequest.getPassword())) {
            String basic = loginRequest.getLoginCode().concat(":").concat(loginRequest.getPassword());
            sessionOperatorRedisTemplate.opsForValue().set("operator:basic:" + Base64Utils.encode(basic), sessionModel);
        }
        return token;
    }


    /**
     * 根据账号密码获取对应的token
     *
     * @param loginCode
     * @param password
     * @return
     */
    public String getToken(String loginCode, String password) throws Exception {
        BooleanBuilder builder = new BooleanBuilder();
        QUserInfo qUserInfo = QUserInfo.userInfo;
        builder.and(qUserInfo.account.name.eq(loginCode));
        //账号和用户是一对一的，并且账号是不允许重复的。
        UserInfo userInfo = userInfoRepository.findOne(builder).orElseThrow(() -> new RuntimeException("该账号不存在"));
        SessionModel sessionModel = new SessionModel();
        LoginRequest loginRequest = new LoginRequest();
        loginRequest.setLoginCode(loginCode);
        loginRequest.setPassword(password);
        String token = setToken(loginRequest, userInfo, userInfo.getAccount(), sessionModel);
        return token;
    }

    private Set<Resource> getResources(UserInfo userInfo, String token, SessionModel sessionModel) {
        Account account = userInfo.getAccount();
        sessionModel.setToken(token);
        sessionModel.setUserName(userInfo.getName());
        sessionModel.setUserCode(account.getName());
        sessionModel.setId(account.getId());
        sessionModel.setUserId(userInfo.getId());
        Set<RoleInfoResponse> roleSet = new HashSet<>();
        for (RoleInfo roleInfo : account.getRoleInfoSet()) {
            RoleInfoResponse roleInfoResponse = new RoleInfoResponse();
            BeanUtils.copyProperties(roleInfo, roleInfoResponse);
            roleSet.add(roleInfoResponse);
        }
        sessionModel.setRoleInfoSet(roleSet);
        Set<Resource> set = new HashSet<>();
        if (account.getRoleInfoSet() != null && !account.getRoleInfoSet().isEmpty()) {
            for (RoleInfo roleInfo : account.getRoleInfoSet()) {
                set.addAll(roleInfo.getResources());
            }
        }
        return set;
    }


    /**
     * 根据监听事件处理token
     *
     * @param token
     * @return
     * @throws Exception
     */
    public SessionModel listenToken(String token) {
        SessionModel sessionModel = new SessionModel();
        try {
            UserInfo userInfo = userInfoRepository.findOne(QUserInfo.userInfo.account.token.eq(token)).orElseThrow(() -> new RuntimeException("该账号不存在"));
            LoginRequest loginRequest = new LoginRequest();
            setToken(loginRequest, userInfo, userInfo.getAccount(), sessionModel);
        } catch (Exception e) {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletResponse response = attributes.getResponse();
            HttpServletRequest request = attributes.getRequest();
            try {
                response.sendRedirect(request.getContextPath() + "/power");
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return sessionModel;
    }

    /**
     * 监听basic对redis缓存处理
     *
     * @param basic
     * @return
     * @throws Exception
     */
    public SessionModel listenBasic(String basic) {
        SessionModel sessionModel = new SessionModel();
        try {
            String mgs = Base64Utils.decode(basic);
            String[] user = mgs.split(":");
            String loginCode = user[0];
            String password = user[1];
            UserInfo userInfo = userInfoRepository.findOne(QUserInfo.userInfo.account.name.eq(loginCode)).orElseThrow(() -> new RuntimeException("该账号不存在"));
            LoginRequest loginRequest = new LoginRequest();
            loginRequest.setPassword(password);
            loginRequest.setLoginCode(loginCode);
            setToken(loginRequest, userInfo, userInfo.getAccount(), sessionModel);
        } catch (Exception e) {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletResponse response = attributes.getResponse();
            HttpServletRequest request = attributes.getRequest();
            try {
                response.sendRedirect(request.getContextPath() + "/power");
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return sessionModel;
    }

    /**
     * 修改账号密码
     * @param sessionModel
     * @param request
     */
    public void updatePassword(SessionModel sessionModel, PasswordRequest request){
        Optional<Account> optional = accountRepository.findById(sessionModel.getId());
        if(!optional.isPresent()){
            throw new RuntimeException("没有找到当前登录的账号信息");
        }
        Account account = optional.get();
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        if (!passwordEncoder.matches(request.getOldPassword(), account.getPassword())) {
            throw new RuntimeException("旧密码错误");
        }
        String pwd = passwordEncoder.encode(request.getNewPassword());
        account.setPassword(pwd);
        accountRepository.save(account);
    }
}
